package com.senthink.gl.business.userlog;

import com.senthink.gl.business.common.exception.DataAccessException;
import com.senthink.gl.business.userlog.business.LogDeviceDeleteBusiness;
import com.senthink.gl.business.userlog.business.LogDeviceBusiness;
import com.senthink.gl.business.userlog.business.LogOtherBusiness;
import com.senthink.gl.business.userlog.business.LogUserBusiness;
import com.senthink.gl.business.userlog.enums.LogDetailEnum;
import com.senthink.gl.business.userlog.enums.LogOperateEnum;
import com.senthink.gl.business.userlog.enums.LogTypeEnum;
import com.senthink.gl.business.domain.entity.User;
import com.senthink.gl.business.domain.entity.UserLog;
import com.senthink.gl.business.domain.service.UserService;
import com.senthink.gl.business.security.JwtTokenUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author yuanpeng@senthink.com
 * @data 2017/12/14
 */

@Aspect
@Component
public class SystemLogAspect {

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.prefix}")
    private String tokenPrefix;

    @Autowired
    private JwtTokenUtils jwtTokenUtils;
    @Autowired
    private LogUserBusiness logUserBusiness;
    @Autowired
    private LogDeviceBusiness logDeviceBusiness;
    @Autowired
    private UserLogDataService dataService;
    @Autowired
    private UserService userService;
    @Autowired
    private LogDeviceDeleteBusiness logDeviceDeleteBusiness;
    @Autowired
    private LogOtherBusiness logOtherBusiness;

    @Pointcut("@annotation(com.senthink.gl.business.userlog.SystemUserLog)")
    public void userLogAspect() {
    }

    /**
     * 后置记录 用于拦截Service层记录用户的操作
     *
     * @param joinPoint     切点
     * @param systemUserLog 注解信息
     */
    @AfterReturning("userLogAspect()&&@annotation(systemUserLog)")
    public void doAfter(JoinPoint joinPoint, SystemUserLog systemUserLog) {
        if (systemUserLog.operate() != LogOperateEnum.DELETE) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest();

            UserLog log = new UserLog();
            log.setOperate(systemUserLog.operate().getOperate());
            log.setIp(getIpAddr(request));
            if (systemUserLog.operate() == LogOperateEnum.LOGIN_IN) {
                logUserBusiness.main(systemUserLog, log, joinPoint, request);
            } else if (systemUserLog.detail() == LogDetailEnum.ENABLE_USER || systemUserLog.logType() != LogTypeEnum.USER) {
                // 读取session中的用户
                String token = request.getHeader(tokenHeader).substring(tokenPrefix.length());
                String account = jwtTokenUtils.getUsernameFromToken(token);
                User user = userService.findByAccount(account)
                        .orElse(null);
                if (user != null) {
                    log.setUid(user.getUid());
                    log.setRole(user.getRole());
                } else {
                    log.setUid("unknown");
                    log.setRole("unknown");
                }
            }
            if (systemUserLog.logType() == LogTypeEnum.DEVICE) {
                logDeviceBusiness.main(systemUserLog, log, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.POLICIES) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.AREA) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.POLICIES_PARAM) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.USER) {
                logUserBusiness.main(systemUserLog, log, joinPoint, request);
            } else if (systemUserLog.logType() == LogTypeEnum.SYSTEM) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.FAULT) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            }if (systemUserLog.logType() == LogTypeEnum.PARK_DEVICE_TYPE) {
                logDeviceBusiness.main(systemUserLog, log, joinPoint);
            }if (systemUserLog.logType() == LogTypeEnum.ALARM_SET) {
                logDeviceBusiness.main(systemUserLog, log, joinPoint);
            }
            dataService.save(log);
        }
    }

    /**
     * 前置记录 用于拦截Service层记录用户删除操作
     *
     * @param joinPoint
     * @param systemUserLog
     */
    @Before("userLogAspect()&&@annotation(systemUserLog)")
    @Transactional(rollbackFor = DataAccessException.class)
    public void doBefore(JoinPoint joinPoint, SystemUserLog systemUserLog) {
        if (systemUserLog.operate() == LogOperateEnum.DELETE) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest();
            UserLog log = new UserLog();
            log.setOperate(systemUserLog.operate().getOperate());
            log.setIp(getIpAddr(request));
            // 读取session中的用户
            String token = request.getHeader(tokenHeader).substring(tokenPrefix.length());
            String account = jwtTokenUtils.getUsernameFromToken(token);
            User user = userService.findByAccount(account)
                    .orElse(null);
            if (user != null) {
                log.setUid(user.getUid());
                log.setRole(user.getRole());
            } else {
                log.setUid("unknown");
                log.setRole("unknown");
            }
            if (systemUserLog.detail() == LogDetailEnum.DELETE_DEVICE) {
                logDeviceDeleteBusiness.main(systemUserLog, log, request);
            } else if (systemUserLog.detail() == LogDetailEnum.DELETE_AREA) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.POLICIES) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.POLICIES_PARAM) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.USER) {
                logUserBusiness.main(systemUserLog, log, joinPoint, request);
            } else if (systemUserLog.logType() == LogTypeEnum.SYSTEM) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            } else if (systemUserLog.logType() == LogTypeEnum.FAULT) {
                logOtherBusiness.main(systemUserLog, log, request, joinPoint);
            }
            dataService.save(log);
        }
    }

    /**
     * 切入点方法报错时删除前条记录日志
     *
     * @param systemUserLog
     */
    @AfterThrowing("userLogAspect()&&@annotation(systemUserLog)")
    public void afterThrowing(SystemUserLog systemUserLog) {
        if (systemUserLog.operate() != LogOperateEnum.LOGIN_IN) {
            String operate = systemUserLog.operate().getOperate();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest();
            // 读取session中的用户
            String token = request.getHeader(tokenHeader).substring(tokenPrefix.length());
            String account = jwtTokenUtils.getUsernameFromToken(token);
            User user = userService.findByAccount(account)
                    .orElse(null);
            String uid;
            if (user == null) {
                uid = "unknow";
            } else {
                uid = user.getUid();
            }
            UserLog userLog = dataService.findTopByUidAndOperateAndIpOrderByCreateTimeDesc(uid, operate, getIpAddr(request)).orElse(null);
            if (userLog != null) {
                dataService.delete(userLog);
            }
        }
    }

    /**
     * 获取操作用户的ip
     *
     * @param request
     * @return
     */
    private static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }

}
